#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;


namespace UTable.Objects.Handlers.Gestures
{
    public class ThreeContactsGestureRecognizer : GestureRecognizer
    {
        #region Private Fields

        Point _originalPosition1;

        Point _originalPosition2;

        Point _originalPosition3;

        Point _originalHandCenter;

        Point _previousPosition1;

        Point _previousPosition2;

        Point _previousPosition3;

        int _contactId1;

        int _contactId2;

        int _contactId3;

        private Range _contactNumberRange;

        private OneContactStateType _contactState1;

        private OneContactStateType _contactState2;

        private OneContactStateType _contactState3;

        public double MaxHoldDistance;

        public double MaxHandDistance;

        private enum AnchorType
        {
            ContactOneAndTwo,
            ContactOneAndThree,
            ContactTwoAndThree,
            
        }

        private AnchorType anchorType;

        private bool inOneHand;
        #endregion

        #region Constructor

        public ThreeContactsGestureRecognizer()
        {
            _contactNumberRange = new Range(3, 3);

            _contactState1 = _contactState2 = _contactState3 = OneContactStateType.Hold;

            EnabledGestures = new List<ThreeContactsGestureType>();

            MaxHoldDistance = 20;

            MaxHandDistance = 200;

            inOneHand = false;
        }

        #endregion

        #region GestureRecognizer Members

        public List<ThreeContactsGestureType> EnabledGestures;

        public Range ContactNumberRange
        {
            get { return _contactNumberRange; }
        }

        public int Priority
        {
            get { return 10; }
        }

        public bool IsSupportedGesture(Type gesture)
        {
            return gesture.IsSubclassOf(typeof(ThreeContactsGesture));
        }

        public void StartRecognize(GestureInfo info)
        {
            _contactId1 = info.Contacts[0].ID;
            _contactId2 = info.Contacts[1].ID;
            _contactId3 = info.Contacts[2].ID;
            _originalPosition1 = _previousPosition1 = info.Contacts[0].CurrentPosition;
            _originalPosition2 = _previousPosition2 = info.Contacts[1].CurrentPosition;
            _originalPosition3 = _previousPosition2 = info.Contacts[2].CurrentPosition;

            double MinLength = (_originalPosition2 - _originalPosition1).Length;            
            _originalHandCenter = new Point((_originalPosition1.X + _originalPosition2.X) / 2, (_originalPosition1.Y + _originalPosition2.Y) / 2);
            anchorType = AnchorType.ContactOneAndTwo;


            //wucj: I think it's better to assume the third finger is moving.
            // just a suggestion:)

            //if ((_originalPosition3 - _originalPosition1).Length < MinLength)
            //{
            //    MinLength = (_originalPosition3 - _originalPosition1).Length;
            //    _originalHandCenter = new Point((_originalPosition3.X + _originalPosition1.X) / 2, (_originalPosition3.Y + _originalPosition1.Y) / 2);
            //    anchorType = AnchorType.ContactOneAndThree;
            //}

            //if ((_originalPosition3 - _originalPosition2).Length < MinLength)
            //{
            //    MinLength = (_originalPosition3 - _originalPosition2).Length;
            //    _originalHandCenter = new Point((_originalPosition3.X + _originalPosition2.X) / 2, (_originalPosition3.Y + _originalPosition2.Y) / 2);
            //    anchorType = AnchorType.ContactTwoAndThree;
            //}
            if (MinLength < MaxHandDistance)
                inOneHand = true;
        }

        public void Recognizing(GestureInfo info)
        {
            // determine the state of each contact
            if (_contactState3 == OneContactStateType.Hold)
            {
                if ((info.Contacts[2].CurrentPosition - _originalPosition3).Length > MaxHoldDistance / 10)   //wucj: I think it's better to assume the third finger is moving.
                {
                    _contactState3 = OneContactStateType.Drag;
                }
            }
            if (_contactState2 == OneContactStateType.Hold)
            {
                if ((info.Contacts[1].CurrentPosition - _originalPosition2).Length > MaxHoldDistance * 10)
                {
                    _contactState2 = OneContactStateType.Drag;
                }
            }
            if (_contactState1 == OneContactStateType.Hold)
            {
                if ((info.Contacts[0].CurrentPosition - _originalPosition1).Length > MaxHoldDistance * 10)
                {
                    _contactState1 = OneContactStateType.Drag;
                }
            }

            if (anchorType == AnchorType.ContactOneAndTwo && _contactState1 == OneContactStateType.Hold && _contactState2 == OneContactStateType.Hold && _contactState3 == OneContactStateType.Drag)
            {
                //contact1 and 2 anchor, contact3 move
                Vector displacement = info.Contacts[2].CurrentPosition - _previousPosition3;

                if (displacement.LengthSquared > 0)
                    GestureDetected(this, new GestureDetectedEventArgs(new AnchorMoveGesture(_originalHandCenter, displacement, info), 1));
            }
            else if (anchorType == AnchorType.ContactOneAndThree && _contactState1 == OneContactStateType.Hold && _contactState3 == OneContactStateType.Hold && _contactState2 == OneContactStateType.Drag)
            {
                //contact1 and 3 anchor, contact2 move
                Vector displacement = info.Contacts[1].CurrentPosition - _previousPosition2;

                if (displacement.LengthSquared > 0)
                    GestureDetected(this, new GestureDetectedEventArgs(new AnchorMoveGesture(_originalHandCenter, displacement, info), 1));
            }
            else if (anchorType == AnchorType.ContactTwoAndThree && _contactState2 == OneContactStateType.Hold && _contactState3 == OneContactStateType.Hold && _contactState1 == OneContactStateType.Drag)
            {
                //contact2 and 3 anchor, contact1 move
                Vector displacement = info.Contacts[0].CurrentPosition - _previousPosition1;

                if (displacement.LengthSquared > 0)
                    GestureDetected(this, new GestureDetectedEventArgs(new AnchorMoveGesture(_originalHandCenter, displacement, info), 1));
            }
            else
            { }
            _previousPosition1 = info.Contacts[0].CurrentPosition;
            _previousPosition2 = info.Contacts[1].CurrentPosition;
            _previousPosition3 = info.Contacts[2].CurrentPosition;
        }

        public void EndRecognize(GestureInfo info)
        {
            _contactState1 = _contactState2 = _contactState3 = OneContactStateType.Hold;
        }

        public LocalGestureEngine GestureEngine
        {
            get;
            set;
        }

        public event GestureDetectedEventHandler GestureDetected;

        #endregion
    }

    public enum ThreeContactsGestureType
    {        
        AnchorMove,
    }

    public class ThreeContactsGesture : Gesture
    {
        public ThreeContactsGesture(GestureInfo info)
        {
            Info = info;
        }
    }

    public class AnchorMoveGesture : ThreeContactsGesture
    {
        public AnchorMoveGesture(Point position, Vector displacement, GestureInfo info): base(info)
        {
            this.AnchorPosition = position;
            this.Displacement = displacement;
        }

        public Point AnchorPosition { get; private set; }

        public Vector Displacement { get; private set; }

        public override string ToString()
        {
            return "AnchorMove :" + AnchorPosition + "; " + Displacement;
        }
    }
}
